﻿using System;
using System.Collections.Generic;
using TiaoTiaoCode.Common.Extensions;

namespace TiaoTiaoCode.Modbuser
{
    /// <summary>
    /// 指令生成器
    /// </summary>
    internal class CommandBuilder
    {
        /// <summary>
        /// 构造读取指令
        /// </summary>
        /// <param name="address"></param>
        /// <param name="length"></param>
        /// <param name="station"></param>
        /// <param name="isStartWithZero"></param>
        /// <param name="defaultFunction"></param>
        /// <returns></returns>
        public static byte[][] BuildReadCommand(string address, ushort length, byte station, bool isStartWithZero, byte defaultFunction)
        {
            var mAddr = new ModbusAddress(address, station, defaultFunction);
            CheckModbusAddressStart(mAddr, isStartWithZero);

            return BuildReadCommand(mAddr, length);
        }

        /// <summary>
        /// 构造写指令
        /// </summary>
        /// <param name="address"></param>
        /// <param name="value"></param>
        /// <param name="station"></param>
        /// <param name="isStartWithZero"></param>
        /// <param name="defaultFunction"></param>
        /// <returns></returns>
        public static byte[] BuildWriteCommand(string address, byte[] value, byte station, bool isStartWithZero, byte defaultFunction)
        {
            var mAddr = new ModbusAddress(address, station, defaultFunction);
            if (mAddr.Function == ModbusCommand.ReadRegister) mAddr.Function = defaultFunction;

            CheckModbusAddressStart(mAddr, isStartWithZero);

            return BuildWriteOneRegisterModbusCommand(mAddr, value);
        }

        static byte[][] BuildReadCommand(ModbusAddress mAddress, ushort length)
        {
            List<byte[]> commands = new List<byte[]>();
            if (mAddress.Function == ModbusCommand.ReadCoil ||
                mAddress.Function == ModbusCommand.ReadDiscrete ||
                mAddress.Function == ModbusCommand.ReadRegister ||
                mAddress.Function == ModbusCommand.ReadInputRegister)
            {
                // 支持自动切割读取，字读取 120 个字，位读取 2000 个位
                var bytes = SplitReadLength(mAddress.Address, length,
                    (mAddress.Function == ModbusCommand.ReadCoil || mAddress.Function == ModbusCommand.ReadDiscrete) ? (ushort)2000 : (ushort)120);
                for (int i = 0; i < bytes.Item1.Length; i++)
                {
                    byte[] buffer = new byte[6];
                    buffer[0] = (byte)mAddress.Station;
                    buffer[1] = (byte)mAddress.Function;
                    buffer[2] = BitConverter.GetBytes(bytes.Item1[i])[1];
                    buffer[3] = BitConverter.GetBytes(bytes.Item1[i])[0];
                    buffer[4] = BitConverter.GetBytes(bytes.Item2[i])[1];
                    buffer[5] = BitConverter.GetBytes(bytes.Item2[i])[0];
                    commands.Add(buffer);
                }
            }
            else
            {
                byte[] buffer = new byte[6];
                buffer[0] = (byte)mAddress.Station;
                buffer[1] = (byte)mAddress.Function;
                buffer[2] = BitConverter.GetBytes(mAddress.Address)[1];
                buffer[3] = BitConverter.GetBytes(mAddress.Address)[0];
                buffer[4] = BitConverter.GetBytes(length)[1];
                buffer[5] = BitConverter.GetBytes(length)[0];
                commands.Add(buffer);
            }
            return commands.ToArray();
        }

        static void CheckModbusAddressStart(ModbusAddress mAddress, bool isStartWithZero)
        {
            if (!isStartWithZero)
            {
                if (mAddress.Address < 1) throw new Exception("地址值在起始地址为1的情况下，必须大于1");

                mAddress.Address = (ushort)(mAddress.Address - 1);
            }
        }

        static (int[], int[]) SplitReadLength(int address, ushort length, ushort segment)
        {
            int[] segments = length.TiaoTiaoSplitIntegerToArray((int)segment);
            int[] addresses = new int[segments.Length];
            for (int i = 0; i < addresses.Length; i++)
            {
                if (i == 0) addresses[i] = address;
                else addresses[i] = addresses[i - 1] + segments[i - 1];
            }
            return (addresses, segments);
        }

        static byte[] BuildWriteOneRegisterModbusCommand(ModbusAddress mAddress, byte[] value)
        {
            // short ushort
            if (value.Length <= 2)
            {
                byte[] content = new byte[6];
                content[0] = (byte)mAddress.Station;
                if (mAddress.WriteFunction < 0)
                    content[1] = (byte)mAddress.Function;
                else
                    content[1] = (byte)mAddress.WriteFunction;
                content[2] = BitConverter.GetBytes(mAddress.Address)[1];
                content[3] = BitConverter.GetBytes(mAddress.Address)[0];
                content[4] = value[1];
                content[5] = value[0];

                return content;
            }
            // int long float double uint ulong
            else
            {
                byte[] content = new byte[7 + value.Length];
                content[0] = (byte)mAddress.Station;
                if (mAddress.WriteFunction < 0)
                    content[1] = (byte)mAddress.Function;
                else
                    content[1] = (byte)mAddress.WriteFunction;
                content[2] = BitConverter.GetBytes(mAddress.Address)[1];
                content[3] = BitConverter.GetBytes(mAddress.Address)[0];
                content[4] = (byte)(value.Length / 2 / 256);
                content[5] = (byte)(value.Length / 2 % 256);
                content[6] = (byte)(value.Length);
                value.CopyTo(content, 7);

                return content;
            }
        }
    }
}
